package com.eagle.core.aop.service;

import com.eagle.core.annotation.SelfAspect;
import com.eagle.core.annotation.SelfService;
import com.eagle.core.aop.SelfProxy;
import com.eagle.core.aop.SelfProxyFactory;
import com.eagle.core.aop.SelfTransactionProxy;
import com.eagle.core.utils.CommonUtils;

import java.util.*;
import java.util.stream.Collectors;

public class AopService {
	private Map<String, Object> ioc;
	private List<String> clazzNameList;

	public AopService(Map<String, Object> ioc, List<String> clazzNameList) {
		this.ioc = ioc;
		this.clazzNameList = clazzNameList;

	}

	public Map<String, Object> createProxy() throws Exception {
		Map<String, Object> aopMap = new HashMap<>();
		//切面类-目标类集合的映射
		Map<Class<?>, Set<Class<?>>> aspectMap = this.createAspectMap();
		//目标类-切面代理对象列表的映射
		Map<Class<?>, List<SelfProxy>> targetMap = createTargetMap(aspectMap);
		//把切面对象织入到目标类中, 创建代理对象
		for (Map.Entry<Class<?>, List<SelfProxy>> targetEntry : targetMap.entrySet()) {
			Class<?> targetClass = targetEntry.getKey();
			List<SelfProxy> targetProxyList = targetEntry.getValue();
			Object proxy = SelfProxyFactory.createProxy(targetClass, targetProxyList);
			String iocKey = CommonUtils.toLowerFirstWord(targetClass.getSimpleName());
			aopMap.put(iocKey,proxy);
		}
		return aopMap;
	}

	private Map<Class<?>, List<SelfProxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception{
		Map<Class<?>, List<SelfProxy>> targetMap = new HashMap<Class<?>, List<SelfProxy>>();
		for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : aspectMap.entrySet()) {
			//切面类
			Class<?> aspectClass = proxyEntry.getKey();
			//目标类集合
			Set<Class<?>> targetClassSet = proxyEntry.getValue();
			//创建目标类-切面对象列表的映射关系
			for (Class<?> targetClass : targetClassSet) {
				//切面对象
				SelfProxy aspect = (SelfProxy) aspectClass.newInstance();
				if (targetMap.containsKey(targetClass)) {
					targetMap.get(targetClass).add(aspect);
				} else {
					//切面对象列表
					List<SelfProxy> aspectList = new ArrayList<SelfProxy>();
					aspectList.add(aspect);
					targetMap.put(targetClass, aspectList);
				}
			}
		}
		return targetMap;
	}

	/**
	 * 获取切面类-目标类集合的映射
	 */
	private Map<Class<?>, Set<Class<?>>> createAspectMap() throws Exception {
		Map<Class<?>, Set<Class<?>>> aspectMap = new HashMap<Class<?>, Set<Class<?>>>();
		this.addAspectProxy(aspectMap);
		addTransactionProxy(aspectMap);
		return aspectMap;
	}

	/**
	 *  获取事务切面类-目标类集合的映射
	 */
	private void addTransactionProxy(Map<Class<?>, Set<Class<?>>> aspectMap) {
		Set<Class<?>> serviceClassSet = new HashSet<>();
		//所有SelfService注解的类
		Collection<Object> clazzSet = this.ioc.values();
		for (Object clazz : clazzSet) {
			Class<?> instance = clazz.getClass();
			if (instance.isAnnotationPresent(SelfService.class)) {
				serviceClassSet.add(instance);
			}
		}
		aspectMap.put(SelfTransactionProxy.class, serviceClassSet);
	}

	/**
	 *  获取普通切面类-目标类集合的映射
	 */
	private void addAspectProxy(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
		//所有实现了SelfAspectProxy抽象类的切面
		Collection<Object> clazzSet = this.ioc.values();
		for (Object clazz : clazzSet) {
			Class<?> instance = clazz.getClass();
			if (instance.isAnnotationPresent(SelfAspect.class)) {
				SelfAspect aspect = instance.getAnnotation(SelfAspect.class);
				//与该切面对应的目标类集合
				Set<Class<?>> targetClassSet = this.createTargetClassSet(aspect);
				aspectMap.put(instance, targetClassSet);
			}
		}
	}

	private Set<Class<?>> createTargetClassSet(SelfAspect aspect) {
		Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
		// 包名
		String pkg = aspect.pkg();
		// 类名
		String cls = aspect.cls();
		// 如果包名与类名均不为空，则在ioc容器中查找并添加指定类
		if (!pkg.equals("") && !cls.equals("")) {
			Object iocBean = this.ioc.get(CommonUtils.toLowerFirstWord(cls));
//			this.doAutowired(iocBean);
			if(null != iocBean){
				targetClassSet.add(iocBean.getClass());
			}
		} else if (!pkg.equals("")) {
			// 如果包名不为空, 类名为空, 则ioc容器中查找并添加该包名下所有类
			List<String> targetClazzNameSet = this.clazzNameList.stream().filter(s -> s.indexOf(pkg) > -1)
					.collect(Collectors.toList());
			for (String fullName : targetClazzNameSet) {
				int startIndex = fullName.lastIndexOf(".");
				String simpleName = fullName.substring(startIndex+1, fullName.length());
				Object iocBean = this.ioc.get(CommonUtils.toLowerFirstWord(simpleName));
				if(null != iocBean){
					targetClassSet.add(iocBean.getClass());
				}
			}
		}
		return targetClassSet;
	}
}
